import * as step from '@flow-step/step-toolkit'
import {call} from './exec/call'

export function appendErrorInfo(line: string, stderr: string[], stdout: string[]) {
    if (line.includes('error') || line.includes('fatal')) {
        step.error(line)
        stderr.push(line)  // 错误信息存入独立数组，否则即使git命令失败，输出结果仍不为空
    } else {
        stdout.push(line)
    }
}

export class GitTools {
    constructor() {}

    async clone(
        workPath: string,
        projectDir: string,
        cloneUrl: string,
        branch: string,
        arg1: string,
        arg2: string,
        arg3: string
    ) {
        let params = ['-c', 'http.sslVerify=false', 'clone', cloneUrl]
        if (arg1 != '') {
            params.push(arg1)
        }
        if (arg2 != '') {
            params.push(arg2)
        }
        if (arg3 != '') {
            params.push('--config')
            params.push(arg3)
        }
        if (branch != '') {
            params.push('--branch')
            params.push(branch)
        }
        params.push(projectDir)
        return this.executeGitClone('git', params, workPath)
    }

    async fetch(
        workPath: string,
        name?: string | undefined,
        branch?: string | undefined
    ) {
        let params: string[] = ['fetch']
        if (name != undefined && branch != undefined) {
            params = ['-c', 'http.sslVerify=false', 'fetch', name, branch]
        }
        if (name != undefined && branch === undefined) {
            params = ['-c', 'http.sslVerify=false', 'fetch', name]
        }
        
        try {
            // 直接调用 call 以便检测具体错误
            const result = new GitOutput()
            const stdout: string[] = []
            const stderr: string[] = []
            let errorDetail = '';
            
            result.exitCode = await call('git', params, workPath, {
                listener: {
                    stdoutLine: (line: string) => {
                        stdout.push(line)
                    },
                    stderrLine: (line: string) => {
                        appendErrorInfo(line, stderr, stdout)
                        // 记录错误信息
                        if (line.includes('有歧义的参数') || 
                            line.includes('ambiguous argument') || 
                            line.includes('did not match') || 
                            line.includes('未匹配任何') ||
                            line.includes('错误') ||
                            line.includes('致命错误') ||
                            line.includes('fatal') ||
                            line.includes('error')) {
                            errorDetail = line;
                        }
                    }
                }
            })
            
            result.stdout = stdout.join()
            
            // 任何错误都直接抛出异常，让上层处理重新克隆
            if (result.exitCode !== 0 || errorDetail) {
                const errorMsg = errorDetail || 'Git fetch failed';
                step.error(`Fetch error detected: ${errorMsg}`);
                throw new Error(errorMsg);
            }
            
            return result;
        } catch (error) {
            // 记录错误并重新抛出，确保上层能捕获到
            step.error(`Fetch error: ${error instanceof Error ? error.message : String(error)}`);
            throw error;
        }
    }

    async checkout(workPath: string, branch: string) {
        const params = ['checkout', branch]
        return this.executeGit('git', params, workPath)
    }

    async submodule(
        workPath: string,
        action: string,
        ops1: string,
        ops2?: string,
        ops3?: string
    ) {
        let params = ['submodule', action, ops1]
        if (ops2 != undefined) {
            params.push(ops2)
        }
        if (ops3 != undefined) {
            params.push(ops3)
        }
        return this.executeGit('git', params, workPath)
    }

    async reset(
        workPath: string,
        arg?: string | undefined,
        action?: string | undefined
    ) {
        let params: string[] = ['reset']
        if (arg != undefined && action === undefined) {
            params = ['reset', arg]
        } else if (arg === undefined && action != undefined) {
            params = ['reset', action]
        } else if (arg != undefined && action != undefined) {
            params = ['reset', action, arg]
        }
        return this.executeGit('git', params, workPath)
    }

    async updateConfig(action: string = 'add', workPath: string, url1: string, url2: string ) {
        const params = ['config', '--' + action, '--global', url1, url2]
        const result = new GitOutput()
        const stdout: string[] = []

        result.exitCode = await call('git', params, workPath, {
            listener: {
                stdoutLine: (line: string) => {
                    stdout.push(line)
                },
                stderrLine: (line: string) => {
                    stdout.push(line)
                }
            }
        })
        result.stdout = stdout.join()
        return result
    }

    async sshCommand(workPath: string, arg: string) {
        const params = ['config', 'core.sshCommand', arg]
        return this.executeGit('git', params, workPath)
    }

    async getRemoteUrl(workPath: string) {
        const params = ['config', '--get', 'remote.origin.url']
        return this.executeGit('git', params, workPath)
    }

    async updateRemoteUrl(workPath: string, url: string) {
        const params = ['config', 'remote.origin.url', url]
        return this.executeGit('git', params, workPath)
    }

    async getlocalBranches(workPath: string) {
        const params = ['branch']
        return this.executeGit('git', params, workPath)
    }

    async deleteBranch(workPath: string, branch: string) {
        const params = ['branch', '-D', branch]
        return this.executeGit('git', params, workPath)
    }

    async log(
        workPath: string,
        num: number,
        prettyFormat: string,
        since?: string | undefined,
        until?: string | undefined
    ) {
        let params = ['log', `-${num}`, `--pretty=format:'${prettyFormat}'`]
        if (since != undefined && until != undefined) {
            params = [
                'log',
                `-${num}`,
                `--pretty=format:'${prettyFormat}'`,
                `${since}..${until}`
            ]
        }
        return this.executeGitCommandWithOutputToArray('git', params, workPath)
    }

    async getCommitId(workPath: string, arg: string) {
        const params = ['rev-parse', arg]
        return this.executeGit('git', params, workPath)
    }

    async listFiles(
        workPath: string,
        arg1?: string | undefined,
        arg2?: string | undefined
    ) {
        let params = ['ls-files']
        if (arg1 != undefined) {
            params = ['ls-files', arg1]
        }
        if (arg1 != undefined && arg2 != undefined) {
            params = ['ls-files', arg1, arg2]
        }
        return this.executeGit('git', params, workPath)
    }

    async setEnvs(workPath: string, key: string, value: string) {
        const params = ['config', '--global', key, value]
        return this.executeGit('git', params, workPath);
    }

    async executeGit(
        command: string,
        params: string[],
        options?: string
    ): Promise<GitOutput> {
        const result = new GitOutput()
        const stdout: string[] = []
        const stderr: string[] = []

        result.exitCode = await call(command, params, options, {
            listener: {
                stdoutLine: (line: string) => {
                    stdout.push(line)
                },
                stderrLine: (line: string) => {
                    appendErrorInfo(line, stderr, stdout)
                }
            }
        })
        result.stdout = stdout.join()
        return result
    }

    async executeGitCommandWithOutputToArray(
        command: string,
        params: string[],
        options?: string
    ): Promise<GitOutputToArray> {
        const result = new GitOutputToArray()
        const stdout: string[] = []
        const stderr: string[] = []

        result.exitCode = await call(command, params, options, {
            listener: {
                stdoutLine: (line: string) => {
                    stdout.push(line)
                },
                stderrLine: (line: string) => {
                    appendErrorInfo(line, stderr, stdout)
                }
            }
        })
        result.stdout = stdout
        return result
    }

    async executeGitClone(
        command: string,
        params: string[],
        options?: string
    ): Promise<GitOutput> {
        const result = new GitOutput()
        const stdout: string[] = []
        const stderr: string[] = []

        result.exitCode = await call(command, params, options, {
            listener: {
                stdoutLine: (line: string) => {
                    //step.info(line)
                    stdout.push(line)
                },
                stderrLine: (line: string) => {
                    appendErrorInfo(line, stderr, stdout)
                }
            }
        })
        result.stdout = stdout.join("\n")
        return result
    }
}

export class GitOutput {
    stdout = ''
    exitCode = 0
}

export class GitOutputToArray {
    stdout: string[] = []
    exitCode = 0
}
